﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using static Enums.Timer;

public static partial class Utility
{
    /// <summary>
    /// 协程工具库
    /// </summary>
    public static class Coroutine
    {
        public const float QUARTER_TIME = 0.25f;

        public static WaitForSeconds QuarterWaitSecond = new WaitForSeconds(QUARTER_TIME);
        public static WaitForEndOfFrame WaitForEndOfFrame = new WaitForEndOfFrame();

        private static Dictionary<float, WaitForSeconds> m_WfsDic = new Dictionary<float, WaitForSeconds>() { };
        private static Dictionary<float, WaitForSecondsRealtime> m_RealWfsDic = new Dictionary<float, WaitForSecondsRealtime>() { };

        public static WaitForSeconds GetDelay(float seconds)
        {
            if (!m_WfsDic.TryGetValue(seconds, out var wait))
            {
                wait = new WaitForSeconds(seconds);
                m_WfsDic[seconds] = wait;
            }
            return wait;
        }

        private static WaitForSecondsRealtime wait;
        private static WaitForSeconds wait2;

        private static float step = 0.016f;

        public static IEnumerator GetRealtimeDelay(float seconds)
        {
            wait = wait ?? new WaitForSecondsRealtime(step);
            return DelayTime(seconds);
        }

        public static IEnumerator GetTimeDelay(float seconds)
        {
            wait2 = wait2 ?? new WaitForSeconds(step);
            return DelayTime(seconds);
        }

        public static IEnumerator DelayTime(float seconds)
        {
            var st = Time.time;
            while (Time.time - st <= seconds)
            {
                yield return null;
            }
        }

        public static IEnumerator DelayRealTime(float seconds)
        {
            var st = Time.realtimeSinceStartup;
            while (Time.realtimeSinceStartup - st <= seconds)
            {
                yield return null;
            }
        }

        /// <summary>
        /// 计时器（帧）
        /// </summary>
        /// <param name="action">执行方法</param>
        /// <returns></returns>
        public static IEnumerator Update(Action action)
        {
            while (true)
            {
                yield return null;
                action?.Invoke();
            }
        }

        /// <summary>
        /// 计时器（秒）
        /// </summary>
        /// <param name="interval">每次计时执行间隔时间</param>
        /// <param name="action">执行方法</param>
        /// <returns></returns>
        public static IEnumerator Update(float interval, Action action)
        {
            WaitForSeconds wait = new WaitForSeconds(interval);
            while (true)
            {
                yield return wait;
                action?.Invoke();
            }
        }

        /// <summary>
        /// 真实时间计时器
        /// </summary>
        /// <param name="interval"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static IEnumerator UpdateReal(float interval, Action action, bool immediate = false)
        {
            float timer = 0;
            if (immediate)
            {
                action?.Invoke();
            }

            while (true)
            {
                yield return null;
                timer += Time.unscaledDeltaTime;
                if (timer > interval)
                {
                    timer -= interval;
                    action?.Invoke();
                }
            }
        }

        /// <summary>
        /// 计时器（秒）
        /// </summary>
        /// <param name="time">执行次数</param>
        /// <param name="interval">每次计时执行间隔时间</param>
        /// <param name="action">执行方法</param>
        /// <returns></returns>
        public static IEnumerator Update(int time, float interval, Action action)
        {
            WaitForSeconds wait = new WaitForSeconds(interval);
            int count = 0;
            while (count < time)
            {
                yield return wait;
                action?.Invoke();
                count++;
            }
        }

        /// <summary>
        /// 延迟执行（秒）
        /// </summary>
        /// <param name="time">延迟时间</param>
        /// <param name="action">执行方法</param>
        /// <returns></returns>
        public static IEnumerator Delay(float time, Action action)
        {
            yield return new WaitForSeconds(time);
            action?.Invoke();
        }

        /// <summary>
        /// 延迟一帧执行
        /// </summary>
        /// <param name="action">执行方法</param>
        /// <returns></returns>
        public static IEnumerator DelayFrame(Action action)
        {
            yield return null;
            action?.Invoke();
        }

        /// <summary>
        /// 当前帧的最后执行
        /// </summary>
        /// <param name="action">执行方法</param>
        /// <returns></returns>
        public static IEnumerator DelayEndFrame(Action action)
        {
            yield return WaitForEndOfFrame;
            action?.Invoke();
        }

        /// <summary>
        /// 计时器（帧）
        /// </summary>
        /// <param name="time">总时间</param>
        /// <param name="updateAction">每次计时执行方法</param>
        /// <param name="isScale">是否缩放增量时间</param>
        /// <returns></returns>
        public static IEnumerator Timer(float time, Action<float> updateAction, bool isScale = true)
            => Timer(time, updateAction, null, isScale);

        /// <summary>
        /// 计时器（帧）
        /// </summary>
        /// <param name="time">总时间</param>
        /// <param name="updateAction">每次计时执行方法</param>
        /// <param name="completeAction">完成执行方法</param>
        /// <param name="isScale">是否缩放增量时间</param>
        /// <returns></returns>
        public static IEnumerator Timer(float time, Action<float> updateAction, Action completeAction, bool isScale = true)
        {
            float elapsedTime = 0;
            while (elapsedTime < time)
            {
                elapsedTime += isScale ? Time.deltaTime : Time.unscaledDeltaTime;
                updateAction?.Invoke(elapsedTime);
                yield return null;
            }

            completeAction?.Invoke();
        }

        /// <summary>
        /// 倒计时（帧）
        /// </summary>
        /// <param name="time">总时间</param>
        /// <param name="updateAction">每次计时执行方法</param>
        /// <param name="isScale">是否缩放增量时间</param>
        /// <returns></returns>
        public static IEnumerator CountDown(float time, Action<float> updateAction, bool isScale = true)
            => CountDown(time, updateAction, null, isScale);

        /// <summary>
        /// 倒计时（帧）
        /// </summary>
        /// <param name="time">总时间</param>
        /// <param name="updateAction">每次计时执行方法</param>
        /// <param name="completeAction">完成执行方法</param>
        /// <param name="isScale">是否缩放增量时间</param>
        /// <returns></returns>
        public static IEnumerator CountDown(float time, Action<float> updateAction, Action completeAction, bool isScale = true)
        {
            float leftTime = time;
            float space = QUARTER_TIME;
            while (leftTime > 0)
            {
                space += isScale ? Time.deltaTime : Time.unscaledDeltaTime;
                if (space > QUARTER_TIME)
                {
                    leftTime -= space;
                    updateAction?.Invoke(leftTime);
                    space = 0;
                }
                yield return null;
            }
            yield return null;
            completeAction?.Invoke();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="startTime"></param>
        /// <param name="totalTime">为-1时表示无限计时</param>
        /// <param name="updateAction"></param>
        /// <param name="completeAction"></param>
        /// <param name="isScale"></param>
        /// <returns></returns>
        public static IEnumerator CountAdd(long startTime, float totalTime, Action<float> updateAction, Action completeAction, bool isScale = true)
        {
            float space = QUARTER_TIME;
            long serverTime = 0;
            float leftTime = (serverTime - startTime).MsToSecond();
            while (totalTime == -1 || leftTime <= totalTime)
            {
                space += isScale ? Time.deltaTime : Time.unscaledDeltaTime;
                if (space > QUARTER_TIME)
                {
                    leftTime += space;
                    updateAction?.Invoke(leftTime);
                    space = 0;
                }
                yield return null;
            }
            completeAction?.Invoke();
        }

        /// <summary>
        /// 开始序列帧动画
        /// </summary>
        /// <param name="image"></param>
        /// <returns></returns>
        public static IEnumerator StartSequence(Image image, string[] picNames)
        {
            int index = 0;
            WaitForSeconds wait = new WaitForSeconds(1f / picNames.Length);
            while (true)
            {
                image.SetSprite(picNames[index]);
                index++;
                if (index >= picNames.Length)
                    index = 0;
                yield return wait;
            }
        }

        public static IEnumerator DelayUntil(Func<bool> func, Action action)
        {
            yield return new WaitUntil(func);
            action?.Invoke();
        }

    }
}
